package edu.mapi.aamas.renderer;

import java.awt.AlphaComposite;
import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.GraphicsConfiguration;
import java.awt.Image;
import java.awt.RenderingHints;
import java.awt.geom.Rectangle2D;
import java.util.Enumeration;

import javax.media.j3d.Canvas3D;
import javax.media.j3d.J3DGraphics2D;
import javax.media.j3d.Transform3D;
import javax.media.j3d.View;
import javax.swing.ImageIcon;
import javax.vecmath.Point2d;
import javax.vecmath.Point3d;
import javax.vecmath.Vector2f;
import javax.vecmath.Vector3f;


/**
 * @author RJPG
 * this class is extended to canvas3D of Java3Dm, the difference 
 * is the rewrited method PostRenderer.
 * Is used to display 2d graphiscs over the 3D rederer scene.  
 */
class MyCanvas3D extends Canvas3D {
	//private GraphicsContext3D gc;
	private static final long serialVersionUID = 1L;
	public J3DGraphics2D g2 = null;

	private Dimension d = new Dimension(0, 0);

	protected Camera3D camera;

	//private Obj3D obj = null;

	public Point2d p1 = new Point2d();

	public Point2d p2 = new Point2d();

	public boolean caminfo = false;

	public boolean axisinfo = true;

	public boolean objinfo = false;

	public boolean transpinfo = false;
	
	public boolean veicleinfo = true;
	
	public boolean reguainfo = false;

	public boolean selected = false;
	

	/**
	 * 
	 * @param point3d point in 3D space
	 * @return the 2d coodinates in canvas form point3d in space
	 * it as to be an camera3D assiciated to this canvas 
	 */
	public Point2d get3DTo2DPoint(Point3d point3d) {

		Transform3D temp = new Transform3D();
		this.getVworldToImagePlate(temp);
		temp.transform(point3d);
		//System.out.println(point3d.z);
		
		Point2d point2d = new Point2d();
		if (point3d.z>0.0)
				return point2d;
		this.getPixelLocationFromImagePlate(point3d, point2d);

		return point2d;
	}

	public Point2d get3DTo2DPoint(Point3d point3d,double dist) {

		Transform3D temp = new Transform3D();
		this.getVworldToImagePlate(temp);
		temp.transform(point3d);
		//System.out.println(point3d.z);
		
		Point2d point2d = new Point2d();
		if(camera.projection==View.PARALLEL_PROJECTION)
			dist=0.0;
		if (point3d.z>-dist)
				return point2d;
		this.getPixelLocationFromImagePlate(point3d, point2d);

		return point2d;
	}

	/**
	 * Constructor 
	 * @param gcfg screen configuration
	 * @param cam Camera3D to be associated
	 */
	public MyCanvas3D(GraphicsConfiguration gcfg, Camera3D cam) {
		super(gcfg);
		camera = cam;
		loadIcons();
	}
	/**
	 *	rewrited method from Canvas3D (of Java3D) 
	 */
	public void postRender() {

		if (g2 == null) {
			g2 = this.getGraphics2D();
			/*g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
			 RenderingHints.VALUE_ANTIALIAS_ON);*/
			g2.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING,
					RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
			/*float strokeThickness = 3.0f;
			 BasicStroke stroke = new BasicStroke(strokeThickness);
			 g2.setStroke(stroke);*/

		}

		this.getSize(d);

		//    if(selected) selected(d.width, d.height, g2);  //ver esta parte
		if (caminfo)
			camInfo(d.width, d.height, g2);
		if (reguainfo)
			reguaInfo(d.width, d.height, g2);
		if (axisinfo)
			axisInfo(d.width, d.height, g2);
		if (veicleinfo)
			vehicleInfo(d.width, d.height, g2);
		
		if (camera.associatedrender.gtext)
			gridInfo(d.width, d.height, g2);
		//drawDemo(d.width, d.height, g2);

		defaultDisplay(d.width, d.height, g2);
		
		g2.flush(true);
	}
	
	public Image plain_icon;
	
	
	public void loadIcons()
	{
		plain_icon=new ImageIcon(this.getClass().getClassLoader().getResource("cursors/crosshair_cursor.png")).getImage();
		
	}

	public void vehicleInfo(int w, int h, J3DGraphics2D g2) {
		g2.setStroke(new BasicStroke(1, BasicStroke.CAP_ROUND,
				BasicStroke.JOIN_ROUND));
		g2.setColor(Color.BLACK);
		Point2d orig = get3DTo2DPoint(new Point3d(0.0, 0.0, 0.0));
		//g2.drawString("O",(int)orig.x,(int)orig.y);
		//camera.associatedrender.objects
		for (Enumeration<String> enuma = camera.associatedrender.Vehicles.keys(); enuma
				.hasMoreElements();) {
			String vt =  enuma.nextElement();
			Obj3D obj2 = (Obj3D) camera.associatedrender.Vehicles.get(vt);

			orig = get3DTo2DPoint(new Point3d(obj2.pos));
			if(orig.y!=0 && orig.x!=0)
			{
				String s=vt+"("+(int)obj2.pos[0]+","+(int)obj2.pos[1]+","+(int)obj2.pos[2]+")";
				Rectangle2D stringBounds = g2.getFontMetrics()
				.getStringBounds(s, g2);
				window((int) orig.x - 2, (int) orig.y - 12, (int) orig.x
						+ (int) stringBounds.getWidth(), (int) orig.y+14);
				g2.drawString(s, (int) orig.x, (int) orig.y);
				
				
				if(obj2.destination==null)
				{
					s="Loiter";
					stringBounds = g2.getFontMetrics()
					.getStringBounds(s, g2);
					//window((int) orig.x - 2, (int) orig.y - 12, (int) orig.x
					//		+ (int) stringBounds.getWidth(), (int) orig.y+14);
					g2.drawString(s, (int) orig.x, (int) orig.y+12);
				}
				else
				{
					s="("+(int)obj2.destination.x+","+(int)obj2.destination.y+","+(int)obj2.destination.z+")";
					stringBounds = g2.getFontMetrics()
					.getStringBounds(s, g2);
					//window((int) orig.x - 2, (int) orig.y - 12, (int) orig.x
					//		+ (int) stringBounds.getWidth(), (int) orig.y+14);
					g2.drawString(s, (int) orig.x, (int) orig.y+12);
				}
			}
		}
	}


	public void defaultDisplay(int w, int h, J3DGraphics2D g2) {
		g2.setColor(Color.BLACK);
//		Point2d orig = get3DTo2DPoint(new Point3d(0.0, 0.0, 0.0));
		//g2.drawString("O",(int)orig.x,(int)orig.y);
		//camera.associatedrender.objects
		
		
	}
	
	/**
	 * draw objects info on their position
	 * @param w  width of bitmap 
	 * @param h  heigth of bitmap
	 * @param g2 draw obj
	 */
	

	public void gridInfo(int w, int h, J3DGraphics2D g2) {
		double scale = camera.associatedrender.gspacing ;

		String units = " m";

		if (scale < 1) {
			units = "cm";
			scale *= 100;
		} else {
			if (scale > 1000) {
				units = "Km";
				scale /= 1000;
			}
		}
		int scaleint = (int) (scale * 100);
		scale = scaleint;
		scale /= 100;
		String text = new String(scale + units);
		window(w-70, h-35, w-9, h-10);
		
		g2.drawString(text, (int) w-60, (int) h - 12);
		g2.drawString("Grid Unit", (int) w-60, (int) h - 24);
	}
	
	

	
	/***
	 * 
	 * draw information about the distance between p1 and p2 
	 */
	public void reguaInfo(int w, int h, J3DGraphics2D g2) {
		g2.setColor(Color.BLACK);

		g2.drawLine((int) p1.x, (int) p1.y, (int) p2.x, (int) p2.y);

		Point3d point3d1 = new Point3d();
		Point3d point3d2 = new Point3d();
		camera.canvas.getPixelLocationInImagePlate(p1, point3d1);
		Transform3D temp = new Transform3D();
		camera.canvas.getImagePlateToVworld(temp);
		temp.transform(point3d1);
		camera.canvas.getPixelLocationInImagePlate(p2, point3d2);
		temp.transform(point3d2);
		double scale = point3d2.distance(point3d1);

		String units = " m";

		if (scale < 1) {
			units = "cm";
			scale *= 100;
		} else {
			if (scale > 1000) {
				units = "Km";
				scale /= 1000;
			}
		}
		int scaleint = (int) (scale * 100);
		scale = scaleint;
		scale /= 100;
		String text = new String(scale + units);
		if (camera.projection == View.PERSPECTIVE_PROJECTION) {
			text = new String("(Prespective view)");
		}

		Rectangle2D stringBounds = g2.getFontMetrics()
				.getStringBounds(text, g2);

		window((int) p2.x - 2 + 2, (int) p2.y - 14, (int) p2.x
				+ (int) stringBounds.getWidth(), (int) p2.y);
		g2.drawString(text, (int) p2.x + 2, (int) p2.y - 2);

	}

	/**
	 * 
	 * draw axis information
	 */
	public void axisInfo(int w, int h, J3DGraphics2D g2) {
		g2.setColor(Color.BLACK);
		Cam c = new Cam();
		if (camera.lockobj == null)
			c.camdef(20, camera.psi + Math.PI / 2, camera.theta,
					-(camera.phi - Math.PI));
		else
			c.camdef(		20,
							(camera.psi + camera.lockobj.yaw) + Math.PI / 2,
							(camera.theta/*-camera.lockobj.pitch*/),
							-((camera.phi/*+camera.lockobj.roll*/) - Math.PI));
		//System.out.println("psi="+camera.psi+" theta="+camera.theta+" phi="+camera.phi);//ok...
		int xr1 = w - 78, yr1 = 8, xr2 = w - 8, yr2 = 89;
		window(xr1, yr1, xr2, yr2);
		int xc = w - 58, yc = 51;
		g2.drawLine(xr1 + 10, yr1 + 10, xr2 - 10, yr1 + 10); // desenhar
		g2.drawLine(xr1 + 10, yr1 + 5, xr1 + 10, yr1 + 15); // desenhar
		g2.drawLine(xr2 - 10, yr1 + 5, xr2 - 10, yr1 + 15); // desenhar
		if (camera.projection == View.PERSPECTIVE_PROJECTION) {
			g2.drawLine(xr1 + 22, yr1 + 7, xr1 + 22, yr1 + 13); // desenhar
			g2.drawLine(xr2 - 22, yr1 + 7, xr2 - 22, yr1 + 13); // desenhar
			g2.drawLine(xr1 + 31, yr1 + 9, xr1 + 31, yr1 + 11); // desenhar
			g2.drawLine(xr2 - 31, yr1 + 9, xr2 - 31, yr1 + 11); // desenhar
			//g2.drawString("Presp",xr1+15,yr1+25);
		} else {
			int x1 = xr1 + 10, y1 = yr1 + 10;
			int x2 = xr2 - 10, y2 = yr1 + 10;
			Point3d point3d1 = new Point3d();
			camera.canvas.getPixelLocationInImagePlate(x1, y1, point3d1);
			Transform3D temp = new Transform3D();
			camera.canvas.getImagePlateToVworld(temp);
			temp.transform(point3d1);
			//System.out.println("x1:"+point3d1.x+"y1:"+point3d1.y+"z1:"+point3d1.z);

			Point3d point3d2 = new Point3d();
			camera.canvas.getPixelLocationInImagePlate(x2, y2, point3d2);

			temp.transform(point3d2);
			// System.out.println("x2:"+point3d2.x+"y2:"+point3d2.y+"z2:"+point3d2.z);

			double scale = point3d2.distance(point3d1);
			// System.out.println("dist:"+scale);

			String units = " m";

			if (scale < 1) {
				units = "cm";
				scale *= 100;
			} else {
				if (scale > 1000) {
					units = "Km";
					scale /= 1000;
				}
			}
			int scaleint = (int) (scale * 100);
			scale = scaleint;
			scale /= 100;

			g2.drawString(scale + "", xr1 + 15, yr1 + 25);
			g2.drawString(units, xr1 + 45, yr1 + 25);
		}

		Vector2f vec2a = null;
		Vector2f vec2b = null;
		//-----------------zz
		Vector3f vec3a = new Vector3f(0, 0, -8);
		Vector3f vec3b = new Vector3f(0, 0, 0);
		vec2a = c.to2d(vec3a);
		vec2b = c.to2d(vec3b);

		//vec2a.x=(float) ((vec2a.x*Math.cos(camera.phi))-(vec2a.y*Math.sin(camera.phi)));
		//vec2a.y=(float) ((vec2a.x*Math.sin(camera.phi))-(vec2a.y*Math.cos(camera.phi)));

		int x1 = (int) (3 * vec2a.x);
		int y1 = (int) (3 * vec2a.y);
		int x2 = (int) (3 * vec2b.x);
		int y2 = (int) (3 * vec2b.y);

		x1 += xc; // translate
		y1 += yc;
		x2 += xc;
		y2 += yc;

		g2.drawLine(x1, y1, x2, y2); // desenhar
		g2.drawString("Z", x1 - 5, y1);

		//---------------------------------yy
		vec3a = new Vector3f(0, 8, 0);
		vec3b = new Vector3f(0, 0, 0);
		vec2a = c.to2d(vec3a);
		vec2b = c.to2d(vec3b);
		x1 = (int) (3 * vec2a.x);
		y1 = (int) (3 * vec2a.y);
		x2 = (int) (3 * vec2b.x);
		y2 = (int) (3 * vec2b.y);

		x1 += xc; // translate
		y1 += yc;
		x2 += xc;
		y2 += yc;

		g2.drawLine(x1, y1, x2, y2); //cima
		g2.drawString("Y", x1 - 5, y1);
		//------------------------xx
		vec3a = new Vector3f(8, 0, 0);
		vec3b = new Vector3f(0, 0, 0);
		vec2a = c.to2d(vec3a);
		vec2b = c.to2d(vec3b);
		x1 = (int) (3 * vec2a.x);
		y1 = (int) (3 * vec2a.y);
		x2 = (int) (3 * vec2b.x);
		y2 = (int) (3 * vec2b.y);

		x1 += xc; // translate
		y1 += yc;
		x2 += xc;
		y2 += yc;

		g2.drawLine(x1, y1, x2, y2); //cima
		g2.drawString("X", x1 - 5, y1);
		//g2.drawLine(0,40,40,40);  //cima

	}

	/**
	 *
	 * draw associated camera information  
	 */
	public void camInfo(int w, int h, J3DGraphics2D g2) {

		String text="";

			Point3d vec = new Point3d();
			vec.x += camera.pivot.x;
			vec.y += camera.pivot.y;
			vec.z += camera.pivot.z;
			text = "Target:(" + (double) ((int) (vec.x * 100)) / 100 + ","
					+ (double) ((int) (vec.y * 100)) / 100 + ","
					+ (double) ((int) (vec.z * 100)) / 100 + ")";
			//text="Target:("+(double)((int)(camera.pivot.x*100))/100+","+(double)((int)(camera.pivot.y*100))/100+","+(double)((int)(camera.pivot.z*100))/100+")";
	
		window(8, 8, 210, 37);

		g2.drawString(text, 15, 20);
		
		text = "View type: " + camera.getStrType();
		g2.drawString(text, 118, 33);
		//alpha = 0.0f;
		//g2.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, alpha));
		//g2.drawRoundRect(1,1,130,50,5,5);
		//Shape asda=new Shape();

		//g2.setColor(Color.MAGENTA);

		//g2.drawLine(130,1,130,40);
	}
	
	
	public void selected(int w, int h, J3DGraphics2D g2) {
		//String text;
		g2.setColor(Color.LIGHT_GRAY);
		g2.drawLine(2, 2, w - 3, 2); //cima
		g2.drawLine(2, h - 3, w - 3, h - 3); //dir
		g2.drawLine(2, 2, 2, h - 3); //esq
		g2.drawLine(w - 3, 2, w - 3, h - 3); // baixo
		//this.doLayout();

	}

	/**
	 * call the original postRender of Canvas3D
	 *
	 */
	public void truePostRender() {
		super.postRender();
	}

	/**
	 * draw a transparent rectangle over 3d scene 
	 * @param xr1 x1 corner coordinate   
	 * @param yr1 x1 corner coordinate
	 * @param xr2 x2 corner coordinate
	 * @param yr2 y2 corner coordinate
	 */
	private void window(int xr1, int yr1, int xr2, int yr2) {
		//---------------------------------caixa
		float alpha = 0.5f;
		g2.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER,
				alpha));
		g2.setPaint(new Color(225, 225, 220));
		g2.fillRect(xr1, yr1, xr2 - xr1, yr2 - yr1);
		alpha = 1.0f;
		g2.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER,
				alpha));
		g2.setColor(Color.BLACK);
		g2.drawLine(xr1, yr1, xr2, yr1);
		g2.drawLine(xr1, yr1, xr1, yr2);
		g2.drawLine(xr1, yr2, xr2, yr2);
		g2.drawLine(xr2, yr1, xr2, yr2);
		//----------------------------------fim caixa
	}
	
	/**
	 * @author RJPG
	 *
	 * this class simulate a camera looking to (0,0,0) 
	 * and its use to convert Vector3d -> Vector2d 
	 * based on spherical cooredinates and roll of viewpoint.
	 * (this class doesnt use Java3D or any other 3D engine)   
	 */
	private class Cam {
		private double v11, v12, v13, v21, v22, v23, v32, v33, v43;

		protected double rho, theta, phi, psi;

		//public Vector3f target;

		public float dist;

		public double pidiv180 = (Math.atan(1) / 45);

		Cam() {
			rho = 40;
			theta = Math.PI / 4;
			phi = Math.PI / 4;

			//theta*=pidiv180;
			//phi*=pidiv180;

			coeff();
			dist = 1;
		//	target = new Vector3f(0, 0, 0);
		}

		private void camdef(double rhod, double thetad, double phid, double psid) {
			rho = rhod;
			theta = thetad;
			phi = phid;
			psi = psid;
			//theta*=pidiv180;
			//phi*=pidiv180;

			coeff();

		}

		private void coeff() {
			double costh, sinth, cosph, sinph;
			costh = Math.cos(theta);
			sinth = Math.sin(theta);
			cosph = Math.cos(phi);
			sinph = Math.sin(phi);

			v11 = -sinth;
			v12 = -cosph * costh;
			v13 = -sinph * costh;
			v21 = costh;
			v22 = -cosph * sinth;
			v23 = -sinph * sinth;
			v32 = sinph;
			v33 = -cosph;
			v43 = rho;
		}

		private Vector3f eyecoord(Vector3f pw) {
			Vector3f pe = new Vector3f();
			pe.x = (float) (v11 * pw.x + v21 * pw.y);
			pe.y = (float) (v12 * pw.x + v22 * pw.y + v32 * pw.z);
			pe.z = (float) (v13 * pw.x + v23 * pw.y + v33 * pw.z + v43);

			return pe;
		}

		private Vector2f perspective(Vector3f p) {
			Vector3f pe = null;
			pe = eyecoord(p);
			Vector2f pxy = new Vector2f();
			Vector3f prot = new Vector3f();
			prot.x = (float) ((pe.x * Math.cos(psi)) - (pe.y * Math.sin(psi)));
			prot.y = (float) ((pe.x * Math.sin(psi)) + (pe.y * Math.cos(psi)));
			prot.z = pe.z;
			//prot.x=pe.x;
			//prot.y=pe.y;
			pxy.x = prot.x;///prot.z;
			pxy.y = prot.y;///prot.z;
			return pxy;
		}

		public Vector2f to2d(Vector3f p) {
			Vector2f aux = null;
			aux = perspective(p);
			Vector2f rt = new Vector2f();
			rt.x = (float) (dist * aux.x + 5.);
			rt.y = (float) (-dist * aux.y + 5.);
			return rt;
		}
	}
}
